/////////////////////////////////////////////////////////////////////////////
// Name:        ttsprocess.cpp
// Purpose:     Helpers for external process TTS handlers
// Author:      Julian Smart
// Modified by: 
// Created:     2009-02-10
// RCS-ID:      
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"
#include "wx/txtstrm.h"

#include "tts/tts_process.h"

/*
 * wxTTSExternalCommandHandlerProcess base class declaration
 */

IMPLEMENT_CLASS(wxTTSExternalCommandHandlerProcess, wxProcess)

void wxTTSExternalCommandHandlerProcess::OnTerminate(int pid, int status)
{
    // show the rest of the output
    while ( HasInput() )
        ;

    if (m_handler)
        m_handler->SetProcess(NULL);

    // wxFile::Flush can cause system error messages on Mac,
    // but they seem to be harmless, so switch off logging
    // temporarily
    wxLog::EnableLogging(false);
    
    // send termination event
    wxProcess::OnTerminate(pid, status);

    wxLog::EnableLogging(true);
    
    // Deleted by OnTerminate
    // delete this;
}

bool wxTTSExternalCommandHandlerProcess::HasInput()
{
    bool hasInput = false;

    if ( IsInputAvailable() )
    {
        wxTextInputStream tis(*GetInputStream());

        // this assumes that the output is always line buffered
        wxString msg = tis.ReadLine() ;

        if (m_handler)
            m_handler->WriteOutput(msg);

        hasInput = true;
    }

    if ( IsErrorAvailable() )
    {
        wxTextInputStream tis(*GetErrorStream());

        // this assumes that the output is always line buffered
        wxString msg = tis.ReadLine() ;

        // Make sure we don't output *** if the line is empty
        wxString err = msg;
        err.Trim(true);
        if (!err.IsEmpty())
        {
            msg = wxT("*** ") + msg;
            msg.Replace(wxT("\n"), wxT("\n*** "));
        }

        if (m_handler)
            m_handler->WriteOutput(msg);

        hasInput = true;
    }

    return hasInput;
}

/*
 * wxTTSExternalCommandHandler implementation
 */

IMPLEMENT_CLASS( wxTTSExternalCommandHandler, wxTTSHandler )

int wxTTSExternalCommandHandler::sm_stopping = 0;

wxTTSExternalCommandHandler::wxTTSExternalCommandHandler(const wxString& id, const wxString& displayName): wxTTSHandler(id, displayName)
{
    Init();
}

wxTTSExternalCommandHandler::~wxTTSExternalCommandHandler()
{
}

// Member initialisation
void wxTTSExternalCommandHandler::Init()
{
    m_errorCode = 0;
    m_process = NULL;
    m_pid = 0;
}

// Stop speaking
bool wxTTSExternalCommandHandler::Stop()
{
    if (IsSpeaking() && (m_pid != 0))
    {
        wxKill(m_pid, wxSIGKILL);

#ifdef __WXMSW__

        // Wait for the process to be stopped. We can do this by
        // only processing wxWM_PROC_TERMINATED messages.

        #define wxWM_PROC_TERMINATED (WM_USER+10000)

        while (IsSpeaking())
        {
            wxMilliSleep(10);

            MSG msg;
            if (::PeekMessage(& msg, NULL, wxWM_PROC_TERMINATED, wxWM_PROC_TERMINATED, PM_REMOVE))
            {
                ::TranslateMessage(& msg);
                ::DispatchMessage(& msg);
            }
        }
#else
        sm_stopping ++;

        while (IsSpeaking())
        {
            wxMilliSleep(10);

            // FIXME: causes recursive yield assertions.
            // However, we need some kind of message loop
            // here or we won't guarantee to return after
            // speaking has finished.

            // If we still get yield assertions, the problem
            // may be that several UI events have been queued up
            // and by now it's too late to disable windows to avoid
            // getting further events. So one approach might be to
            // somehow throw away all UI events within the event loop, while
            // we're pending a Stop.
            // Can't use wxSafeYield since it loses the focus, which causes
            // another speak event, which causes this code to be called, etc.

            wxYield();
        }

        sm_stopping --;
#endif
    }
    return true;
}

// Process any pending input
bool wxTTSExternalCommandHandler::ProcessInput()
{
    if (m_process)
    {
        while (m_process->HasInput())
            ;
    }
    return true;
}

// Launch command
bool wxTTSExternalCommandHandler::Launch(const wxString& cmd, int options)
{
    m_command = cmd;

    if (options & wxTTS_SPEAK_SYNC)
    {
        return (wxExecute(cmd, wxEXEC_SYNC) != -1);
    }
    else
    {
        m_process = new wxTTSExternalCommandHandlerProcess(cmd, this, this);
        m_pid = wxExecute(cmd, wxEXEC_ASYNC, m_process);
        
        if ( !m_pid )
        {
            // wxLogError( _("Execution of '%s' failed."), cmd.c_str() );
            m_pid = 0;
            
            delete m_process;
            m_process = NULL;
            return false;
        }
        else
        {
            if (options & wxTTS_SPEAK_SYNC_YIELD)
            {
                do
                {
                    wxSafeYield();
                } while (m_process != NULL);
            }

            return true;
        }
    }
}

bool wxTTSExternalCommandHandler::VoicesAreCompatible(const wxString& WXUNUSED(voice1), const wxString& WXUNUSED(voice2)) const
{
#ifdef __WXMSW__
    return true;
#else
    // Multiple voices can cause more recursive yield problems
    // on Unix.
    return false;
#endif
}
